/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2014-2015, Open Source Geospatial Foundation (OSGeo)
 *    (C) 2014 TOPP - www.openplans.org.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.process.raster;

import java.util.Collection;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.parameter.ParameterValueGroup;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.coverage.processing.operation.BandMerge;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.locationtech.jts.geom.Geometry;

/**
 * Process calling the {@link BandMerge} operation. This process requires:
 *
 * <ul>
 *   <li>a {@link Collection} of {@link GridCoverage2D} objects (Note that they must be in the same
 *       CRS).
 *   <li>an optional ROI passed as {@link SimpleFeature}.
 *   <li>an optional String indicating the policy for choosing the Grid To World transformation(from
 *       those of all the Coverages) to use for the final coverage. The available values are:
 *       FIRST(default), for selecting the first coverage; LAST, for the last coverage; INDEX, for
 *       selecting the Coverage defined by the Index Parameter.
 *   <li>an optional integer parameter called Index used by the Grid To World transformation policy
 *       for choosing the coverage at the "Index" position.
 * </ul>
 *
 * The output of this process is a {@link GridCoverage2D} object which contains all the input
 * Coverages, each one stored as a Band (or multiple bands if the coverage is multibanded). This
 * process can be used also for merging coverages which are not aligned and with different
 * resolutions.
 *
 * @author Nicola Lagomarsini, GeoSolutions S.A.S.
 */
@DescribeProcess(
        title = "Merge Coverages",
        description =
                "Returns a raster generated by the merge of the input raster bands. Source rasters must have the same CRS.")
public class BandMergeProcess implements RasterProcess {

    /** Processor to use for executing the {@link BandMerge} operation */
    private static final CoverageProcessor PROCESSOR = CoverageProcessor.getInstance();

    @DescribeResult(name = "result", description = "Merged Rasters")
    public GridCoverage2D execute(
            @DescribeParameter(
                            name = "coverages",
                            description = "Coverage List",
                            min = 1,
                            collectionType = GridCoverage2D.class)
                    Collection<GridCoverage2D> coverages,
            @DescribeParameter(name = "roi", description = "Geometry to use as ROI", min = 0)
                    Geometry roi,
            @DescribeParameter(
                            name = "transformChoice",
                            description = "Choice on which Coverage G2W transform to use",
                            min = 0)
                    String transformChoice,
            @DescribeParameter(
                            name = "index",
                            description = "Index used by the transformChoice parameter",
                            min = 0)
                    Integer index)
            throws ProcessException {
        // //
        //
        // Initialization: CRS checks
        //
        // //
        BaseCoverageAlgebraProcess.checkCompatibleCoveragesForMerge(coverages);

        // //
        //
        // Doing the Operation
        //
        // //
        final ParameterValueGroup param = PROCESSOR.getOperation("BandMerge").getParameters();
        // //
        //
        // ROI extraction
        //
        // //
        Geometry geo = null;
        if (roi != null) {
            geo = roi;

            Object crsGeo = geo.getUserData();

            if (crsGeo != null && crsGeo instanceof CoordinateReferenceSystem) {
                CoordinateReferenceSystem geoCRS = (CoordinateReferenceSystem) crsGeo;
                GridCoverage2D cov = coverages.iterator().next();
                // CRS Check
                BaseCoverageAlgebraProcess.checkCompatibleCRS(
                        geoCRS, cov.getCoordinateReferenceSystem());
                // Setting of the ROI if present
                param.parameter(BandMerge.GEOMETRY).setValue(geo);
            } else {
                // Supposing that the Geometry has the same CRS of the Coverage
                param.parameter(BandMerge.GEOMETRY).setValue(geo);
            }
        }

        // Addition of all the Coverages as source
        param.parameter("sources").setValue(coverages);

        // Addition of the Transformation Choice parameter if present
        if (transformChoice != null && !transformChoice.isEmpty()) {
            param.parameter(BandMerge.TRANSFORM_CHOICE).setValue(transformChoice);
        }

        // Addition of the Index parameter to use by the Transformation Choice if present
        if (transformChoice != null && !transformChoice.isEmpty()) {
            param.parameter(BandMerge.COVERAGE_INDEX).setValue(index);
        }

        // Call the "BandMerge" operation
        return (GridCoverage2D) PROCESSOR.doOperation(param);
    }
}
